#include "dspSimulator/instruction/ADDKPC.h"

namespace dspsimulator {
    void ADDKPC::generate_ir(const std::shared_ptr<simulator::IRGenElement> &ir_gen_element) {
        std::shared_ptr<simulator::LLVMJit> llvm_jit = ir_gen_element->m_llvm_jit;
        RegIRCopy &reg_ir_copy = ir_gen_element->m_reg_ir_copy;
        std::shared_ptr<llvm::IRBuilder<>> ir_builder = llvm_jit->ir_builder();
        // ADDKPC (.unit) scst3, dst, nop_num
        auto op1 = std::dynamic_pointer_cast<Immediate32>(m_op1);
        auto op2 = std::dynamic_pointer_cast<SingleReg>(m_op2);
        auto op3 = std::dynamic_pointer_cast<SingleReg>(m_op3);
        std::string pc_name = get_reg_name(op3->reg());
        llvm::Value *pc = reg_ir_copy[pc_name];
        llvm::Value *pce1 = ir_builder->CreateSub(pc, ir_builder->CreateSRem(pc, ir_builder->getInt32(0x20)));
        std::string dst_name = get_reg_name(op2->reg());
        llvm::Value *dst_reg_ptr = IRGenerator::get_global_value_ptr(llvm_jit, dst_name);
        llvm::Value *shl = ir_builder->CreateShl(ir_builder->getInt32(op1->data()), ir_builder->getInt32(2));
        llvm::Value *result = ir_builder->CreateAdd(shl, pce1);
        ir_builder->CreateStore(result, dst_reg_ptr);
    }

    std::shared_ptr<DspInstruction> ADDKPC::decode_standard_instruction(const std::shared_ptr<DspInstructionInfo> &dsp_instruction_info) {
        std::shared_ptr<ADDKPC> addkpc;
        std::bitset<32> &data = dsp_instruction_info->m_standard_bits;
        if (exist_standard_condition(common::extract_bits<4>(data, 28)) &&
            common::check_bits<11>(data, 0b00001011000, 2)) {
            addkpc = std::make_shared<ADDKPC>(dsp_instruction_info->address(), 4);
            addkpc->m_creg_z = common::extract_bits<4>(data, 28);
            u32 dst = common::extract_bits<5>(data, 23).to_ulong();
            u32 src1 = common::extract_bits<7>(data, 16).to_ulong();
            u32 src2 = common::extract_bits<3>(data, 13).to_ulong();
            bool s = data[1];
            addkpc->m_func_unit = FuncUnit::S2;
            addkpc->m_op1 = std::make_shared<Immediate32>(common::extract_bit_to_int<7>(src1));
            addkpc->m_op2 = std::make_shared<SingleReg>(get_dst_reg32_kind(dst, s));
            addkpc->m_op3 = std::make_shared<SingleReg>(RegKind::PC);
            addkpc->m_nop_num = src2;
            addkpc->m_p = data[0];
        }
        return addkpc;
    }

    std::string ADDKPC::to_string() const {
        return m_name + " " + get_function_unit_name(m_func_unit) + m_op1->to_string() +
               "," + m_op2->to_string() + "," + std::to_string(m_nop_num);
    }

    std::shared_ptr<simulator::Instruction> ADDKPC::clone() const {
        return std::make_shared<ADDKPC>(*this);
    }
}